//------------------------------------------------------------
//  filename: eth_mac_rx.sv                                   
//                                                            
//  author  : wlduan@gmail.com                                
//  revise  : 2020-11-13 16:12                                
//  description:                                              
//     the eth mac rx module                                  
//                                                            
//------------------------------------------------------------
//
`timescale 1ns/1ps
 
module eth_mac_rx ( 
    input  logic       rstn_i,

    input  logic       rx_clk,
    input  logic[3:0]  rx_data,
    input  logic       rx_data_v,

    input  logic[47:0] mac_uni_addr,
    output logic[7:0]  mac_rx_data,
    output logic       mac_rx_valid,
    output logic       mac_rx_sof,
    output logic       mac_rx_eof,
    output logic       mac_rx_err,

    input  logic[63:0] mac_rx_hash_tab, //multicast hash table
    input  logic       mac_rx_chk_en,
    input  logic       mac_rx_drop_bcst, // drop broadcast
    input  logic       mac_rx_drop_mcst, // drop multicast
    input  logic       mac_rx_pass_all,
    output logic       mac_rx_addr_miss,

    output logic       mac_ft_abort
);
//-------------------------------------------------------------
logic      clk_i;
logic      nible_h;
logic      sof_capen;
logic      go_drop; // 1.half when trans 2.lenth more than max, 3.ifg not fullfill
logic[7:0] byte_recv,byte_recv_q; 
logic[7:0] recv_data;
logic      recv_valid;
logic      recv_sof;
logic      unicast_ok;
logic      broadcast_ok;
logic      multicast_ok;
logic      ctrl_frm_addr_ok;
//-------------------------------------------------------------
assign go_drop = 0;
assign clk_i   = rx_clk;
//=======================================================
//====
//=======================================================
// rx state machine module
//--------------------------------------------------------
enum logic[7:0] {IDLE,PREAMB,DATA0,DATA1,DONE,DROP} rx_cs,rx_ns;
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        rx_cs <= IDLE;
    end
    else begin
        rx_cs <= rx_ns;
    end
end
//--------------------------------------------------------
always_comb begin
    rx_ns = rx_cs;
    case(rx_cs)
        IDLE: begin
            rx_ns = (rx_data_v&&(rx_data == 4'h5)) ? PREAMB : rx_cs;
        end
        PREAMB: begin
            rx_ns = (rx_data == 4'hd) ? DATA0 : rx_cs; // sfd detected
        end
        DATA0: begin
            rx_ns = rx_data_v ? DATA1 : DONE;
        end
        DATA1: begin
            rx_ns = go_drop ? DROP : DATA0 ;
        end
        DONE: begin
            rx_ns = IDLE;
        end
        DROP: begin
            rx_ns = rx_data_v ? rx_cs : IDLE;
        end
    endcase
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)begin 
        sof_capen <= 'b0;    
    end 
    else if(rx_cs == IDLE) begin 
        sof_capen <= 1'b1;    
    end
    else if(sof_capen&rx_data_v&(rx_cs == DATA1))begin 
        sof_capen <= 1'b0;    
    end 
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)begin 
        byte_recv_q <= 'b0;    
    end 
    else begin 
        byte_recv_q <= byte_recv;    
    end 
end 
//--------------------------------------------------------
assign nible_h = ((rx_cs == DATA1)&rx_data_v);    
//--------------------------------------------------------
always_comb begin
    byte_recv = byte_recv_q;
    byte_recv[4*nible_h +: 4] = rx_data;
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)begin 
        recv_data   <= 'b0;    
        recv_valid  <= 'b0;  
        recv_sof    <= 'b0;  
    end 
    else begin 
        recv_data   <= nible_h ? byte_recv : recv_data; 
        recv_valid  <= nible_h; 
        recv_sof    <= nible_h & sof_capen;  
    end 
end 
//--------------------------------------------------------
logic       crc_en;
logic[3:0]  data_crc;
logic[31:0] crc_next;
logic       crc_init;
logic[31:0] crc;
logic       crc_ok;
logic       crc_en_ff1;
//=======================================================
//====
//=======================================================
assign crc_en   = (rx_cs == DATA0)|(rx_cs == DATA1);
assign crc_init = (rx_cs == PREAMB);
//--------------------------------------------------------
assign {data_crc[0],data_crc[1],data_crc[2],data_crc[3]} = {rx_data[3],rx_data[2],rx_data[1],rx_data[0]};
//--------------------------------------------------------
assign crc_next[0]  =  crc_en & (data_crc[0] ^ crc[28]); 
assign crc_next[1]  =  crc_en & (data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29]); 
assign crc_next[2]  =  crc_en & (data_crc[2] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[30]); 
assign crc_next[3]  =  crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[1] ^ crc[29] ^ crc[30] ^ crc[31]); 
assign crc_next[4]  = (crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[0] ^ crc[28] ^ crc[30] ^ crc[31])) ^ crc[0]; 
assign crc_next[5]  = (crc_en & (data_crc[3] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[31])) ^ crc[1]; 
assign crc_next[6]  = (crc_en & (data_crc[2] ^ data_crc[1] ^ crc[29] ^ crc[30])) ^ crc[ 2]; 
assign crc_next[7]  = (crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[0] ^ crc[28] ^ crc[30] ^ crc[31])) ^ crc[3]; 
assign crc_next[8]  = (crc_en & (data_crc[3] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[31])) ^ crc[4]; 
assign crc_next[9]  = (crc_en & (data_crc[2] ^ data_crc[1] ^ crc[29] ^ crc[30])) ^ crc[5]; 
assign crc_next[10] = (crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[0] ^ crc[28] ^ crc[30] ^ crc[31])) ^ crc[6]; 
assign crc_next[11] = (crc_en & (data_crc[3] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[31])) ^ crc[7]; 
assign crc_next[12] = (crc_en & (data_crc[2] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[30])) ^ crc[8]; 
assign crc_next[13] = (crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[1] ^ crc[29] ^ crc[30] ^ crc[31])) ^ crc[9]; 
assign crc_next[14] = (crc_en & (data_crc[3] ^ data_crc[2] ^ crc[30] ^ crc[31])) ^ crc[10]; 
assign crc_next[15] = (crc_en & (data_crc[3] ^ crc[31])) ^ crc[11]; 
assign crc_next[16] = (crc_en & (data_crc[0] ^ crc[28])) ^ crc[12]; 
assign crc_next[17] = (crc_en & (data_crc[1] ^ crc[29])) ^ crc[13]; 
assign crc_next[18] = (crc_en & (data_crc[2] ^ crc[30])) ^ crc[14]; 
assign crc_next[19] = (crc_en & (data_crc[3] ^ crc[31])) ^ crc[15]; 
assign crc_next[20] =  crc[16]; 
assign crc_next[21] =  crc[17]; 
assign crc_next[22] = (crc_en & (data_crc[0] ^ crc[28])) ^ crc[18]; 
assign crc_next[23] = (crc_en & (data_crc[1] ^ data_crc[0] ^ crc[29] ^ crc[28])) ^ crc[19]; 
assign crc_next[24] = (crc_en & (data_crc[2] ^ data_crc[1] ^ crc[30] ^ crc[29])) ^ crc[20]; 
assign crc_next[25] = (crc_en & (data_crc[3] ^ data_crc[2] ^ crc[31] ^ crc[30])) ^ crc[21]; 
assign crc_next[26] = (crc_en & (data_crc[3] ^ data_crc[0] ^ crc[31] ^ crc[28])) ^ crc[22]; 
assign crc_next[27] = (crc_en & (data_crc[1] ^ crc[29])) ^ crc[23]; 
assign crc_next[28] = (crc_en & (data_crc[2] ^ crc[30])) ^ crc[24]; 
assign crc_next[29] = (crc_en & (data_crc[3] ^ crc[31])) ^ crc[25]; 
assign crc_next[30] =  crc[26]; 
assign crc_next[31] =  crc[27]; 
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)begin 
        crc <= 32'hffffffff;
    end
    else if(crc_init) begin
        crc <= 32'hffffffff;
    end
    else begin
        crc <= crc_next;
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)begin 
        crc_ok <= 'b0;
    end
    else begin
        crc_ok <= (crc[31:0] == 32'hc704dd7b);  // crc not equal to magic number
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)begin 
        crc_en_ff1 <= 'b0;
    end
    else begin
        crc_en_ff1 <= crc_en;  // crc not equal to magic number
    end
end
//=======================================================
//====
//=======================================================
logic[15:0] recv_byte_cnt;
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)begin 
        mac_rx_data   <= 'b0;    
        mac_rx_valid  <= 'b0;  
        mac_rx_sof    <= 'b0; 
        mac_rx_eof    <= 'b0; 
    end 
    else begin 
        mac_rx_data   <= recv_data  ; 
        mac_rx_valid  <= recv_valid ;
        mac_rx_sof    <= recv_sof   ;
        mac_rx_eof    <= (rx_ns == DONE); 
    end 
end 
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        recv_byte_cnt <= 'b0;        
    end 
    else if(nible_h & sof_capen)begin 
        recv_byte_cnt <= 'b0;        
    end 
    else if(nible_h) begin
        recv_byte_cnt <= recv_byte_cnt + 'b1;        
    end
end 
//--------------------------------------------------------
assign mac_rx_err = (~crc_en)&crc_en_ff1&(~crc_ok);
//--------------------------------------------------------
// Address Detection (unicast)
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)
        unicast_ok <=  1'b0;
    else if(recv_valid & (recv_byte_cnt == 0))
        unicast_ok <=  ( recv_data[7:0] == mac_uni_addr[47:40]);
    else if(recv_valid & (recv_byte_cnt == 1))
        unicast_ok <=  ( recv_data[7:0] == mac_uni_addr[39:32]) & unicast_ok;
    else if(recv_valid & (recv_byte_cnt == 2))
        unicast_ok <=  ( recv_data[7:0] == mac_uni_addr[31:24]) & unicast_ok;
    else if(recv_valid & (recv_byte_cnt == 3))
        unicast_ok <=  ( recv_data[7:0] == mac_uni_addr[23:16]) & unicast_ok;
    else if(recv_valid & (recv_byte_cnt == 4))
        unicast_ok <=  ( recv_data[7:0] == mac_uni_addr[15:8])  & unicast_ok;
    else if(recv_valid & (recv_byte_cnt == 5))
        unicast_ok <=  ( recv_data[7:0] == mac_uni_addr[7:0])   & unicast_ok;
    else if(mac_rx_eof | mac_ft_abort)
        unicast_ok <=  1'b0;
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)
        broadcast_ok <=  1'b0;
    else if(recv_valid & (recv_byte_cnt == 0) & (~mac_rx_drop_bcst))
        broadcast_ok <=  ( &recv_data[7:0] );
    else if(recv_valid & (recv_byte_cnt == 1))
        broadcast_ok <=  ( &recv_data[7:0] ) & broadcast_ok;
    else if(recv_valid & (recv_byte_cnt == 2))
        broadcast_ok <=  ( &recv_data[7:0] ) & broadcast_ok;
    else if(recv_valid & (recv_byte_cnt == 3))
        broadcast_ok <=  ( &recv_data[7:0] ) & broadcast_ok;
    else if(recv_valid & (recv_byte_cnt == 4))
        broadcast_ok <=  ( &recv_data[7:0] ) & broadcast_ok;
    else if(recv_valid & (recv_byte_cnt == 5))
        broadcast_ok <=  ( &recv_data[7:0] ) & broadcast_ok;
    else if(mac_rx_eof | mac_ft_abort)
        broadcast_ok <=  1'b0;
end 
//--------------------------------------------------------
logic[5:0]  hs_bit_sel;
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)
        hs_bit_sel <= 6'h0;
    else if(recv_sof)
        hs_bit_sel <= 6'h0;
    else if(recv_valid & (recv_byte_cnt == 5))
        hs_bit_sel <= crc[31:26];
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)
        multicast_ok <=  1'b0;
    else if(recv_valid & (recv_byte_cnt == 0) & (~mac_rx_drop_mcst))
        multicast_ok <=  1'b1;
    else if(recv_valid & (recv_byte_cnt == 6))
        multicast_ok <=  mac_rx_hash_tab[hs_bit_sel] & multicast_ok;
    else if(mac_rx_eof | mac_ft_abort)
        multicast_ok <=  1'b0;
end 
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)
        mac_rx_addr_miss <= 1'b0;
    else if(recv_sof)
        mac_rx_addr_miss <= 1'b0;
    else if((recv_valid & (recv_byte_cnt == 7) & mac_rx_chk_en))
        mac_rx_addr_miss <= (~(unicast_ok | broadcast_ok | multicast_ok | (mac_rx_pass_all & ctrl_frm_addr_ok)));
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) 
        ctrl_frm_addr_ok <= 1'b0;
    else 
        ctrl_frm_addr_ok <= 1'b0;
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)
        mac_ft_abort <= 1'b0;
    else if((~(unicast_ok | broadcast_ok | multicast_ok))&(recv_byte_cnt == 7))
        mac_ft_abort <= 1'b1;
    else if(rx_cs == IDLE)
        mac_ft_abort <= 1'b0;
end
//--------------------------------------------------------

//--------------------------------------------------------
endmodule
